<?php
/*****************************************************
 * File name: ctrl.php
 * Create date: 2017/11/30
 * Author: smallyang
 * modify: ronzheng 2018/12/7
 * 1、对所有请求默认开启登录态校验
 * 2、增加白名单校验机制
 * 3、判断活动是否在线
 * 4、校验活动业务类型以及sappid
 * Description: ctrl 基类
 *****************************************************/

namespace Lib\Base;

use Lib\Paas\Login;

class Ctrl
{
    const LOG_TYPE_INSENT = 1;//操作日志类型 表操作 添加
    const LOG_TYPE_EDIT = 2;//操作日志类型 表操作 编辑
    const LOG_TYPE_DEL = 3;//操作日志类型 表操作 删除
    const LOG_TYPE_LOGIN = 4;//操作日志类型 登陆
    const LOG_TYPE_OTHER = 5;//操作日志类型 其他
    const LOG_TYPE_JSON_UPLOAD = 6;//操作日志类型 json文件上传

    /**
     * 输出json
     *
     * @param $iRetcode int 状态码
     * @param $sErrorMsg string 状态说明
     * @param $vmResult array 返回的数组
     * @return string json
     */
    public function outputJSON($iRetcode = 0, $sErrorMsg = 'ok', $vmResult = array())
    {
        $res = array(
            'iRet' => $iRetcode,
            'sMsg' => $sErrorMsg,
            'jData' => $vmResult,
        );

        if (isset($_GET['sAmsSerial'])) {
            $res['sSerial'] = $_GET['sAmsSerial'];
        }
        recordAMSLog(__FILE__ . "," . __LINE__ . ",output data(before filterHtml):" . var_export($res, true));
        $res = Common::filterHtml($res);
        recordAMSLog(__FILE__ . "," . __LINE__ . ",output data(after filterHtml):" . var_export($res, true));

        echo json_encode($res);

        //echo UTF8toGBK($r);

        exit();
    }

    /**
     * 加载模板和页面输出 可以返回输出内容
     * @access public
     * @param string $templateFile 模板文件名
     * @return mixed
     */
    public function display($templateFile)
    {
        $view = View::init();
        $view->display($templateFile);
    }

    /**
     * 模板变量赋值
     * @access public
     * @param mixed $name
     * @param mixed $value
     */
    public function assign($name, $value='')
    {
        $view = View::init();
        $view->assign($name, $value);
    }

    /**
     * Ajax方式返回数据到客户端
     * @access protected
     * @param mixed $data 要返回的数据
     * @param String $type AJAX返回数据格式
     * @param int $json_option 传递给json_encode的option参数
     * @return void
     */
    protected function ajaxReturn($data, $type = '', $json_option = 0)
    {
        if (empty($type)) {
            $type = "JSON";
        }
        if (isset($_GET['sAmsSerial'])) {
            header("sSerial:".$_GET['sAmsSerial']);
        }

        switch (strtoupper($type)) {
            case 'JSON' :
                // 返回JSON数据格式到客户端 包含状态信息
                header('Content-Type:application/json; charset=utf-8');
                $data = json_encode($data, $json_option);
                break;
            case 'EVAL' :
                // 返回可执行的js脚本
                header('Content-Type:text/html; charset=utf-8');
                break;
        }
        exit($data);
    }

    /**
     * Ajac错误返回
     * @param string $msg
     * @param array $fields
     */
    protected function ajaxError($msg = '', $fields = array())
    {
        if (isset($_GET['sAmsSerial'])) {
            header("sSerial:".$_GET['sAmsSerial']);
        }
        header('Content-Type:application/json; charset=utf-8');
        $data = array('status' => 'error', 'msg' => $msg, 'fields' => $fields);
        echo json_encode($data);
        exit;
    }

    /**
     * Ajac成功返回
     * @param $msg
     * @param array $_data
     */
    protected function ajaxSuccess($msg, $_data = array())
    {
        if (isset($_GET['sAmsSerial'])) {
            header("sSerial:".$_GET['sAmsSerial']);
        }
        header('Content-Type:application/json; charset=utf-8');
        $data = array('status' => 'success', 'msg' => $msg, 'data' => $_data);
        echo json_encode($data);
        exit;
    }

    /**
     * 运行主程序
     */
    public function run()
    {

        // 定义当前请求的系统常量
        define('NOW_TIME',      $_SERVER['REQUEST_TIME']);
        define('REQUEST_METHOD',$_SERVER['REQUEST_METHOD']);
        define('IS_GET',        REQUEST_METHOD =='GET' ? true : false);
        define('IS_POST',       REQUEST_METHOD =='POST' ? true : false);
        define('IS_PUT',        REQUEST_METHOD =='PUT' ? true : false);
        define('IS_DELETE',     REQUEST_METHOD =='DELETE' ? true : false);
        define('IS_AJAX',       ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') || !empty($_POST["ajax"]) || !empty($_GET["ajax"])) ? true : false);

        //生成AmsSerial流水
        if (!isset($_GET['sAmsSerial'])) {
            $_GET['sAmsSerial'] = createAmsSerial();
        }

        //server log
        recordAMSLog(json_encode($_SERVER));

        $actId = Common::getRequestParam('iActId');

        if (!Common::checkIsNum($actId)) {
            recordAMSLog(__FILE__ . "," . __LINE__ . ",活动号错误，活动号：" . $actId);
            $this->outputJSON(-1, '活动ID错误！', array('code' => -102));
        }

        //开启活动级别的限流 add by rongzheng 2018/12/4
        if (defined('REQUESTS_PER_SECOND') && REQUESTS_PER_SECOND > 0) {
            $limitKey = 'ulink_request_limiter_' . $actId;
            try {
                $redis = Redis::init();
                $ret = $redis->accessLimit($limitKey, 1, REQUESTS_PER_SECOND);
                if ($ret != 0) {
                    $this->outputJSON(-888888, '活动太火爆，请稍后再来参加！', array('code' => -888888));
                }
            } catch (\Exception $e) {
                recordAMSLog("在执行限流时初始化redis对象出现异常，错误信息：" . $e->getMessage());
            }
        }

        //路由获取
        $route = trim(strip_tags($_GET['route']));
        if (!$route) {
            recordAMSLog(__FILE__ . "," . __LINE__ . ",route参数错误：" . var_export($route, true));
            $this->outputJSON(-1, '系统繁忙，请稍后再试[' . __LINE__ . ']！', array('code' => -100));
        }
        $routeArr = explode('/', $route);
        if (!$routeArr[0] || !$routeArr[1]) {
            recordAMSLog(__FILE__ . "," . __LINE__ . ",route参数错误，ctrl:" . var_export($routeArr[0], true) . ",action:" . var_export($routeArr[1], true));
            $this->outputJSON(-1, '系统繁忙，请稍后再试[' . __LINE__ . ']！', array('code' => -101));
        }

        //获取ctrl 和 action
        $routeCtrl = $routeArr[0];
        $routeAction = $routeArr[1];

        //定义当前控制器
        define('CONTROLLER_NAME',$routeCtrl);
        //定义当前方法
        define('ACTION_NAME',$routeAction);

        if (ENV != 'test') {
            $actConfig = Common::getActConfig($actId);
            if ($actConfig === false) {
                $this->outputJSON(-1, '系统繁忙，请稍后再试[' . __LINE__ . ']！', array('code' => -104));
            }
            //校验请求参数中的业务类型、SAPPID等信息
            $game = Common::getRequestParam('game');
            if (!isset($actConfig['BASE_INFO']['game']) || \strcmp($actConfig['BASE_INFO']['game'], $game) !== 0) {
                $this->outputJSON(-1, '系统繁忙，请稍后再试[' . __LINE__ . ']！', array('code' => -121));
            }
            //校验SAPPID
            if (!isset($actConfig['BASE_INFO']['sAppId']) || \strcmp($actConfig['BASE_INFO']['sAppId'], ULINK_SAPPID) !== 0) {
                $this->outputJSON(-1, '系统繁忙，请稍后再试[' . __LINE__ . ']！', array('code' => -122));
            }

            //活动如果被强制下线，则给出对应提示
            if (!isset($actConfig['BASE_INFO']['status']) || !in_array($actConfig['BASE_INFO']['status'], ['0', '1'])) {
                $this->outputJSON(-1, '活动太火爆，请稍后再试！', array('code' => -106));
            } else {
                if ($actConfig['BASE_INFO']['status'] == 1) {
                    //判断活动是否在有效期内
                    $nowTime = date('Y-m-d H:i:s');
                    if (!isset($actConfig['BASE_INFO']['sday']) || !isset($actConfig['BASE_INFO']['eday'])) {
                        recordAMSLog(__FILE__ . "," . __LINE__ . ",活动配置文件中缺少sday或者eday,请检查配置文件！");
                        $this->outputJSON(-1, '系统繁忙，请稍后再试[' . __LINE__ . ']！', array('code' => -107));
                    }
                    //校验活动时间
                    if ($nowTime < $actConfig['BASE_INFO']['sday']) {
                        $this->outputJSON(-1, '活动还未开始，敬请期待！', array('code' => -108));
                    }
                    if ($actConfig['BASE_INFO']['eday'] < $nowTime) {
                        $this->outputJSON(-1, '活动已经结束！', array('code' => -109));
                    }
                }
            }
        }

        $login = null;
        //强制开启登录校验
        if (!defined('NO_CHECK_LOGIN') || !in_array($route, NO_CHECK_LOGIN)) {
            $login = new Login();
            if (!$login->checkLogin()) {
                $this->outputJSON(-1, '登录态失效，请重新登录！', array('code' => -103));
            }
        }

        //白名单校验
        if (ENV == 'pre' || (ENV == 'pro' && $actConfig['BASE_INFO']['status'] == '0')) {
            //不需要登录校验的模块不进行白名单校验
            if (!defined('NO_CHECK_LOGIN') || !in_array($route, NO_CHECK_LOGIN)) {
                $login = is_null($login) ? new Login() : $login;
                if (!$login->checkInWhite()) {
                    $this->outputJSON(-1, '活动暂未开放，敬请期待！', array('code' => -105));
                }
            }
        }

        try {
            $CtrlClass = "Ctrl\\" . $routeCtrl;
            if (method_exists($CtrlClass, $routeAction)) {
                (new $CtrlClass())->$routeAction();
            } else {
                outputJSON(-101, $routeAction . '方法不存在');
            }
        } catch (\Exception $e) {
            recordAMSLog(__FUNCTION__ . "_Exception: " . $CtrlClass . ' exception: ' . $e->getMessage());
            throw new \Exception(__FUNCTION__ . "_Exception: " . $CtrlClass . ' exception: ' . $e->getMessage());
        }
    }

}
